#ifndef SWANLINK_FINANCE_KEYBOARD_FRAMEWORKS_JS_NAPI_KEYBOARD_UTILS_H
#define SWANLINK_FINANCE_KEYBOARD_FRAMEWORKS_JS_NAPI_KEYBOARD_UTILS_H

#include <cstdio>
#include <cstdlib>
#include <string>
#include <string.h>
#include "nocopyable.h"
#include "js_native_api.h"
#include "js_native_api_types.h"
#include "napi/native_common.h"
#include "napi/native_api.h"
#include "hilog_wrapper.h"

namespace SwanLink {
namespace Finance {
using namespace OHOS;
#define DEBUG_NAPI_CALL_PROMISE(env, theCall) \
    do {                                     \
        napi_status status_; \
        if ((status_ = (theCall)) != napi_ok) {          \
            KEYBOARD_HILOGE(KEYBOARD_JS_NAPI, "failed to call "#theCall", status = %{public}d", (int)status_); \
            return (AsyncPromise(env, __FUNCTION__, nullptr));                \
        }                                    \
    } while (0)

#define DEBUG_NAPI_CALL(env, theCall) \
    do {                                     \
        napi_status status_;\
        if ((status_ = (theCall)) != napi_ok) {          \
            KEYBOARD_HILOGE(KEYBOARD_JS_NAPI, "failed to call "#theCall", status = %{public}d", (int)status_); \
            return nullptr;                  \
        }                                    \
    } while (0)

#define DEBUG_NAPI_ASSERT(env, assertion, message) \
    do {                                                                                     \
        if (!(assertion)) {                                                                  \
            KEYBOARD_HILOGE(KEYBOARD_JS_NAPI, message); \
            return nullptr;                                                                   \
        }                                                                                    \
    } while (0)

#define DEBUG_NAPI_CALL_NO_RET(env, theCall) \
    do {                                     \
        napi_status status_;\
        if ((status_ = (theCall)) != napi_ok) {          \
            KEYBOARD_HILOGE(KEYBOARD_JS_NAPI, "failed to call "#theCall", status = %{public}d", (int)status_); \
        }                                    \
    } while (0)

#define DEBUG_NAPI_ASSERT_NO_RET(env, assertion, message) \
    do {                                                                                     \
        if (!(assertion)) {                                                                  \
            KEYBOARD_HILOGE(KEYBOARD_JS_NAPI, message); \
        }                                                                                    \
    } while (0)

static inline napi_value priv_get_value(napi_env env, int32_t val) {
    napi_value value;
    napi_create_int32(env, val, &value);
    return value;
}
#define DECLARE_CONST_VAR(var) DECLARE_NAPI_STATIC_PROPERTY(#var, priv_get_value(env, (int32_t)var))

#define NAPI_OBJECT_PROP_GET(obj, type, prop, result) do {\
    napi_value val;  \
    napi_valuetype vtype;\
    DEBUG_NAPI_CALL(env, napi_typeof(env, obj, &vtype));\
    DEBUG_NAPI_ASSERT(env, vtype == napi_object, "Wrong argument typr. Obeject expected");\
    DEBUG_NAPI_CALL(env, napi_get_named_property(env, obj, prop, &val));\
    DEBUG_NAPI_CALL(env, napi_get_value_##type(env, val, result));\
}while(0)

#define NAPI_OBJECT_PROP_GET_WITH_LEN(obj, type, prop, len, result) do {\
    napi_value val;  \
    napi_valuetype otype;\
    napi_valuetype vtype;\
    DEBUG_NAPI_CALL(env, napi_typeof(env, obj, &otype));\
    DEBUG_NAPI_ASSERT(env, otype == napi_object, "Wrong argument typr. Obeject expected");\
    DEBUG_NAPI_CALL(env, napi_get_named_property(env, obj, prop, &val));\
    DEBUG_NAPI_CALL(env, napi_typeof(env, val, &vtype));\
    KEYBOARD_HILOGD(KEYBOARD_JS_NAPI, "attri:%{public}s type is %{public}d", prop, vtype); \
    if (vtype == napi_string) \
        DEBUG_NAPI_CALL(env, napi_get_value_##type(env, val, result, sizeof(result), &len));\
    else \
        result[0] = '\0';\
}while(0)

#define NAPI_OBJECT_PROP_GET_BUFFER(obj, prop, len, result) do {\
    napi_value val, buffer;  \
    napi_valuetype vtype;\
    napi_typedarray_type type;\
    size_t offset = 0;\
    DEBUG_NAPI_CALL(env, napi_typeof(env, obj, &vtype));\
    DEBUG_NAPI_ASSERT(env, vtype == napi_object, "Wrong argument typr. Obeject expected");\
    DEBUG_NAPI_CALL(env, napi_get_named_property(env, obj, prop, &val));\
    DEBUG_NAPI_CALL(env, napi_get_typedarray_info(env, val, &type, reinterpret_cast<size_t*>(&len), (void **)&result, &buffer, &offset));\
}while(0)

#define NAPI_OBJECT_PROP_SET(obj, type, prop, result) do {\
    napi_value val;  \
    DEBUG_NAPI_CALL(env, napi_create_##type(env, result, &val));\
    DEBUG_NAPI_CALL(env, napi_set_named_property(env, obj, prop, val));\
}while(0)

#define NAPI_OBJECT_PROP_SET_WITH_LEN(obj, type, prop, len, result) do {\
    napi_value val;  \
    DEBUG_NAPI_CALL(env, napi_create_##type(env, result, len, &val));\
    DEBUG_NAPI_CALL(env, napi_set_named_property(env, obj, prop, val));\
}while(0)

#define NAPI_OBJECT_PROP_SET_BUFFER(obj, prop, len, result) do {\
    napi_value val;  \
    napi_value arrayBuffer;\
    void *buffer;\
    DEBUG_NAPI_CALL(env, napi_create_arraybuffer(env, len, &buffer, &arrayBuffer));\
    memcpy(buffer, result, len);\
    DEBUG_NAPI_CALL(env, napi_create_typedarray(env, napi_uint8_array, len, arrayBuffer, 0, &val));\
    DEBUG_NAPI_CALL(env, napi_set_named_property(env, obj, prop, val));\
}while(0)

#define MAX_ARGS  8
typedef napi_status (*async_excute_work)(napi_env env, size_t argc, napi_value args[], void *data);
typedef napi_value (*async_result_work)(napi_env env, void *data);
typedef void (*async_free_work)(napi_env env, void *data);

class AsyncCallback {
public:
    AsyncCallback(napi_env env,
        size_t argc, napi_value args[],
        async_excute_work work,
        async_result_work result_work,
        async_free_work free_work,
        void   *data = nullptr,
        napi_value this_arg = nullptr,
        void *priv_data = nullptr);

    ~AsyncCallback();

    DISALLOW_COPY(AsyncCallback);

    napi_status ExcuteWork(napi_env env);
    napi_value ResultWork(napi_env env);
    void FreeWork(napi_env env);

public:
    napi_deferred  deferred;
    napi_ref   ref_callback;
    napi_value arg_this;
    size_t     argc_n;
    napi_value args_v[MAX_ARGS];

    napi_status status;
    async_excute_work   excute_work;
    async_result_work   result_work;
    async_free_work     free_work;
    napi_async_work     async_work;
    void   *data_ptr;
};

// 一般调用
napi_value NapiCall(napi_env &env, AsyncCallback *async);
// 异步回调
napi_value AsyncCall(napi_env &env, napi_value callback, const std::string &res_name, AsyncCallback *async);
napi_value AsyncCall2(napi_env &env, napi_value& callback, const std::string &res_name, AsyncCallback *async);
// 异步Promise
napi_value AsyncPromise(napi_env &env, const std::string& res_name, AsyncCallback *async);
// 同步Promise
napi_value SyncPromise(const napi_env &env, napi_status status, napi_value result);

} // namespace Finance
} // namespace SwanLink
#endif // SWANLINK_FINANCE_KEYBOARD_FRAMEWORKS_JS_NAPI_KEYBOARD_UTILS_H